package com.cloudkinto.service.file.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.bo.file.DeleteFileBo;
import com.cloudkinto.common.common.DateUtils;
import com.cloudkinto.common.common.FileUtils;
import com.cloudkinto.common.constant.GlobalConstants;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.ali.AliOss;
import com.cloudkinto.common.utils.dict.StaticDict;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.*;
import com.cloudkinto.entity.*;
import com.cloudkinto.enumType.operate.OperateTypeEnum;
import com.cloudkinto.service.file.FileService;
import com.cloudkinto.service.operate.OperateRecordService;
import com.cloudkinto.vo.file.FileMsg;
import com.cloudkinto.vo.file.FileRes;
import com.cloudkinto.vo.file.FileVo;
import com.cloudkinto.vo.file.upload.FileMoreUpload;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.activation.MimetypesFileTypeMap;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhangyy
 * @date 2020/5/27 12:35
 * @description:
 */
@Transactional(rollbackFor = Exception.class)//事务回滚
@Service
@Slf4j
@RequiredArgsConstructor
public class FileServiceImpl extends ServiceImpl<FileDao, FileDo> implements FileService {

    private final FileDao fileDao;
    private final FileMoreDao fileMoreDao;
    private final OperateRecordService operateRecordService;
    private final UserDao userDao;
    private final OrderInputDao inputDao;
    private final OrderOutboundDao outboundDao;
    //真正的存储路径
    @Value("${gbc.uploadPath.filePath}")
    private String filePath;
    //真正的存储路径
    @Value("${gbc.uploadPath.fileUrl}")
    private String fileUrl;
    @Value("${gbc.uploadPath.fileUrlTemp}")
    private String fileUrlTemp;
    //临时存储路径
    @Value("${gbc.uploadPath.filePathTemp}")
    private String filePathTemp;
    //临时存储路径
    @Value("${gbc.uploadPath.filePathLocal}")
    private String filePathLocal;

    @Override
    public SingleResult uploadTempFile(MultipartFile file, Long userId) {
        Long fileId = this.uploadTemp(file, userId);
        Map<String, Long> map = new HashMap<>();
        map.put("fileId", fileId);
        return SingleResult.success(map);
    }

    @Override
    public SingleResult uploadTempFileReturnDo(MultipartFile file) {
        if (file.isEmpty()) {
            throw new BizException(SysConstant.Pls_Choose_File);
        }
        String fileName = file.getOriginalFilename();//获取文件名
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//获取文件后缀
        if (fileName.length() > 80) {
            fileName = fileName.substring(fileName.length() - 80);
        }
        if (!GlobalConstants.FILE_SUFFIX.contains(suffix)) {
            throw new BizException(SysConstant.File_Suffix_Incorrect);
        }
        //添加记录file表
        FileDo fileDo = addFile(fileName, suffix);
        Long fileId = fileDo.getId();
        if (filePath.startsWith("http")) {
            //添加到临时文件
            try {
                AliOss.temporaryFile(fileId, suffix, file);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            upload(file, suffix, fileId);
        }
        FileVo vo = new FileVo();
        BeanUtils.copyProperties(fileDo, vo);
        vo.setUrl(this.getFilePathTemp(fileDo.getId()));
        return SingleResult.success(vo);
    }

    public Long uploadTemp(MultipartFile file, Long userId) {
        if (file.isEmpty()) {
            throw new BizException(SysConstant.Pls_Choose_File);
        }
        String fileName = file.getOriginalFilename();//获取文件名
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//获取文件后缀
        if (fileName.length() > 80) {
            fileName = fileName.substring(fileName.length() - 80);
        }
        if (!GlobalConstants.FILE_SUFFIX.contains(suffix)) {
            throw new BizException(SysConstant.File_Suffix_Incorrect);
        }
        //添加记录file表
        Long fileId = addFile(fileName, suffix, userId);
        if (filePath.startsWith("http")) {
            //添加到临时文件
            try {
                AliOss.temporaryFile(fileId, suffix, file);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            upload(file, suffix, fileId);
        }

        return fileId;
    }

    @Override
    public Long uploadTempFile(String fileName, InputStream inputStream, Long userId) {
        //保存到t_file表中
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//获取文件后缀
        if (fileName.length() > 80) {
            fileName = fileName.substring(fileName.length() - 80);
        }
        if (!GlobalConstants.FILE_SUFFIX.contains(suffix)) {
            return 0L;
        }
        Long fileId = addFile(fileName, suffix, userId);
        if (filePath.startsWith("http")) {
            //添加到临时文件
            try {
                AliOss.temporaryFile3(fileId, suffix, inputStream);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            FileUtils.make(filePathTemp);
            String path = filePathTemp + File.separator + fileId + "." + suffix;
            try {
                FileUtils.uploadFile(inputStream, path);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return fileId;
    }

    @Override
    public void uploadFileMult(String[] fileIds) {
        for (String fileId : fileIds) {
            if (StringUtils.isBlank(fileId)) {
                continue;
            }
            uploadFile(Long.parseLong(fileId));
        }
    }


    @Override
    public void uploadFile(Long fileId) {
        if (fileId == null || fileId <= 0) {
            return;
        }
        FileDo fileDo = fileDao.selectById(fileId);
        if (fileDo == null) {
            throw new BizException(SysConstant.File_Upload_Error);
        }
        if (!filePath.startsWith("http")) {
            //临时目录的文件
            File file = new File(filePathTemp + File.separator + fileDo.getId() + "." + fileDo.getFileSuffix());
            if (!file.exists()) {
                return;
            }

            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(file);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return;
            }
            //将临时目录的文件移到真正的目录
            FileUtils.uploadFile(fileInputStream, filePath + File.separator + fileDo.getId() + "." + fileDo.getFileSuffix());
            file.delete();
        } else {
            //上传到阿里OSS 并删除临时保存的文件
            AliOss.uploadFile(AliOss.objectName + "temporaryFile/" + fileId + "." + fileDo.getFileSuffix(), fileDo.getCreateTime(), fileDo.getFileSuffix(), fileDo.getId());
        }
    }

    @Override
    public void uploadFile2(Long fileId, Long oldFileId) {
        if (fileId == null || fileId == 0) {
            return;
        }
        FileDo fileDo = fileDao.selectById(fileId);
        if (fileDo == null) {
            throw new BizException(SysConstant.File_Upload_Error);
        }
        if (filePath.startsWith("http")) {
            fileDo.setCreateBy(oldFileId);
            //上传到阿里OSS 并删除临时保存的文件
            AliOss.uploadFile(AliOss.objectName + "temporaryFile/" + fileId + "." + fileDo.getFileSuffix(), fileDo.getFileSuffix(), fileDo.getId());
        } else {
            File file = new File(filePath + File.separator + oldFileId + "." + fileDo.getFileSuffix());
            if (file != null) {
                file.delete();
            }
            uploadFile(fileId);
        }
    }

    @Override
    public List<String> getFilePathMulti(String fileIds) {
        if (StringUtils.isEmpty(fileIds)) return new ArrayList<>();
        String[] strings = fileIds.split(",");
        List<String> paths = new ArrayList<>();
        for (String fileId : strings) {
            paths.add(getFilePath(Long.parseLong(fileId)));
        }
        return paths;
    }

    @Override
    public Map<Long, String> getFilePath(List<Long> fileIdList) {
        if (CollUtil.isEmpty(fileIdList)) {
            return Collections.emptyMap();
        }
        List<FileDo> fileDoList = fileDao.selectBatchIds(fileIdList);
        if (CollUtil.isEmpty(fileDoList)) {
            return Collections.emptyMap();
        }
        Map<Long, String> map = new HashMap<>();
        for (FileDo fileDo : fileDoList) {
            String path = getFilePathByFile(fileDo);
            if (StringUtils.isBlank(path)) {
                continue;
            }
            map.put(fileDo.getId(), path);
        }
        return map;
    }

    /**
     * 获取文件访问路径
     *
     * @param fileId
     * @return
     */
    @Override
    public String getFilePath(Long fileId) {
        FileDo fileDo = fileDao.selectById(fileId);
        if (fileDo == null) {
            return null;
        }
        return this.getFilePathByFile(fileDo);
    }

    public String getFilePathByFile(FileDo fileDo) {
        if (filePath.startsWith("http")) {
            String date = DateUtils.dateFormatToString(fileDo.getCreateTime(), "yyyyMM");
            //对文件类型进行处理
            if (GlobalConstants.IMG_SUFFIX.contains(fileDo.getFileSuffix())) {
                return fileUrl + date + "/photo/" + fileDo.getId() + "." + fileDo.getFileSuffix();
            }
            return fileUrl + date + "/document/" + fileDo.getId() + "." + fileDo.getFileSuffix();
        } else {
            return fileUrl + fileDo.getId() + "." + fileDo.getFileSuffix();
        }
    }


    @Override
    public String getFilePathTemp(Long fileId) {
        FileDo fileDo = fileDao.selectById(fileId);
        if (fileDo == null) {
            return null;
        }
        if (filePath.startsWith("http")) {
            return AliOss.template + fileDo.getId() + "." + fileDo.getFileSuffix();
        } else {
            return fileUrlTemp + fileDo.getId() + "." + fileDo.getFileSuffix();
        }
    }


    @Override
    public void deleteFileMulti(String[] fileIds) {
        for (String fileId : fileIds) {
            if (StringUtils.isBlank(fileId)) {
                continue;
            }
            deleteFile(Long.parseLong(fileId));
        }
    }

    @Override
    public void deleteFile(Long fileId) {
        if (fileId == null || fileId == 0L) {
            return;
        }
        FileDo fileDo = fileDao.selectById(fileId);
        if (fileDo == null) {
            return;
        }
        if (filePath.startsWith("http")) {
            //得到原图路径  因为以前的数据所以需要判断
            String filePath = getFilePath(fileId);
            AliOss.deleteFile(filePath, fileDo.getFileSuffix(), fileDo.getId());
        } else {
            File file = new File(filePath + File.separator + fileDo.getId() + "." + fileDo.getFileSuffix());
            if (file != null) {
                file.delete();
            }
        }
        fileDao.deleteById(fileId);
    }

    @Override
    public Long uploadExcelImage(String url, Long userId) throws FileNotFoundException {
        //FileUtils.make(filePathTemp);
        File file = new File(url);
        if (!file.exists()) return null;
        String fileName = file.getName();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//获取文件后缀
        Long fileId = addFile(fileName, suffix, userId);
        //临时保存
        try {
            AliOss.temporaryFile2(fileId, suffix, file);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //保存真实的
        AliOss.uploadFile(getFilePathTemp(fileId), suffix, fileId);
        file.delete();
        return fileId;
    }


    //将文件存储到临时目录
    private void upload(MultipartFile file, String suffix, Long fileId) {
        FileUtils.make(filePathTemp);
        String path = filePathTemp + File.separator + fileId + "." + suffix;
        try {
            FileUtils.uploadFile(file.getInputStream(), path);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private Long addFile(String fileName, String suffix, Long userId) {
        FileDo fileDo = new FileDo();
        fileDo.setCreateBy(userId);
        fileDo.setCreateTime(new Date());
        fileDo.setDeleteFlag(SysConstant.DELETE_FLAG_FALSE);
        fileDo.setFileName(fileName);
        fileDo.setFileSuffix(suffix);
        fileDao.insert(fileDo);
        return fileDo.getId();
    }

    private FileDo addFile(String fileName, String suffix) {
        FileDo fileDo = new FileDo();
        fileDo.setCreateTime(new Date());
        fileDo.setDeleteFlag(SysConstant.DELETE_FLAG_FALSE);
        fileDo.setFileName(fileName);
        fileDo.setFileSuffix(suffix);
        fileDao.insert(fileDo);
        return fileDo;
    }

    @Override
    public SingleResult uploadImageBase64(String base64, String fileName, Long userId) {
        if (StringUtils.isBlank(base64)) {
            throw new BizException(SysConstant.Pls_Choose_File);
        }
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//获取文件后缀
        if (!GlobalConstants.FILE_SUFFIX.contains(suffix)) {
            throw new BizException(SysConstant.File_Suffix_Incorrect2);
        }
        //添加记录file表
        Long fileId = addFile(fileName, suffix, userId);
        //添加到临时文件
        try {
            AliOss.temporaryFile(fileId, suffix, base64);
        } catch (Exception e) {
            e.printStackTrace();
        }
        Map map = new HashMap();
        map.put("fileId", fileId);
        return SingleResult.success(map);
    }


    /**
     * 附件上传，二进制流直接上传到正式路径
     *
     * @param inputStream
     * @param fileName
     */
    @Override
    public Long uploadFileToFormalPath(InputStream inputStream, String fileName) {
        Long fileId = this.addFile(fileName);
        FileDo fileDo = fileDao.selectById(fileId);
        if (fileDo == null) {
//            throw new BizException("文件上传失败，请稍后再试！");
            return 0L;
        }
        if (!filePath.startsWith("http")) {
            //直接保存到本地正式路径
            FileUtils.uploadFile(inputStream, filePath + File.separator + fileDo.getId() + "." + fileDo.getFileSuffix());
        } else {
            //直接上传到阿里OSS
            AliOss.uploadFile(inputStream, fileDo.getFileSuffix(), fileId);
        }
        return fileId;
    }


    public Long addFile(String fileName) {
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//获取文件后缀
        if (suffix.length() > 8) {
            suffix = "jpg";
        }
        if (fileName.length() > 80) {
            fileName = fileName.substring(fileName.length() - 80);
        }
        FileDo fileDo = new FileDo();
        fileDo.setDeleteFlag(SysConstant.DELETE_FLAG_FALSE);
        fileDo.setFileName(fileName);
        fileDo.setFileSuffix(suffix);
        fileDao.insert(fileDo);
        return fileDo.getId();
    }


    /**
     * 将网络图片转成File对象
     *
     * @param
     */
    @Override
    public File getFile(String url, String tempFilePath) {
        //对本地文件命名
        File file = null;
        if (!url.startsWith("https")) {
            file = new File(url);
        } else {
            URL urlfile;
            InputStream inStream = null;
            OutputStream os = null;
            try {
                file = new File(tempFilePath);
                //下载
                urlfile = new URL(url);
                inStream = urlfile.openStream();
                os = new FileOutputStream(file);

                int bytesRead = 0;
                byte[] buffer = new byte[4096];
                while ((bytesRead = inStream.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
                os.flush();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != os) {
                        os.close();
                    }
                    if (null != inStream) {
                        inStream.close();
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return file;
    }


    @Override
    public void addFileMoreDo(Long fileId, Long mailId, String type) {
        if (fileId != null && fileId != 0L) {
            FileMoreDo fileMoreDo = new FileMoreDo();
            fileMoreDo.setFileId(fileId);
            fileMoreDo.setMailId(mailId);
            fileMoreDo.setType(type);
            fileMoreDo.setCreateTime(new Date());
            fileMoreDao.insert(fileMoreDo);
        }
    }


    /**
     * 根据附件id，返回二进制流
     *
     * @param fileId
     * @param response
     */
    @Override
    public void getOutput(Long fileId, HttpServletResponse response) {
        FileDo fileDo = this.getById(fileId);
        if (fileDo == null) {
            return;
        }
        File file = this.getFileByFileId(fileId);
        try {
            response.setCharacterEncoding("UTF-8");
            String type = new MimetypesFileTypeMap().getContentType(file);

            response.setHeader("content-Type", type);
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileDo.getFileName(), "UTF-8"));
//            FileInputStream fis;
//            fis = new FileInputStream(file);
//            long size = file.length();
//            byte[] temp = new byte[(int) size];
//            fis.read(temp, 0, (int) size);
//            fis.close();
//            byte[] data = temp;
//            response.getOutputStream().write(data);
            response.getOutputStream().write(Files.readAllBytes(file.toPath()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public File getFileByFileId(Long fileId) {
        File file;
        FileDo fileDo = this.getById(fileId);
        if (fileDo == null) {
            return null;
        }
        String url = this.getFilePath(fileId);
        if (url.startsWith("https")) {
            String tempPath = filePathTemp + File.separator + fileDo.getId() + "." + fileDo.getFileSuffix();
            File tempFile = new File(tempPath);
            if (tempFile.exists()) {
                file = tempFile;
            } else {
                file = this.getFile(url, tempPath);
            }
        } else {
            file = new File(filePathLocal + File.separator + fileDo.getId() + "." + fileDo.getFileSuffix());
        }
        return file;
    }

    @Override
    public FileMsg getFileMsg(Long fileId) {
        if (fileId == null) return null;
        FileMsg fileRes = new FileMsg();
        fileRes.setFileId(fileId);
        FileDo fileDo = fileDao.selectById(fileId);
        if (fileDo == null) return null;
        fileRes.setFileName(fileDo.getFileName());
        fileRes.setFileSuffix(fileDo.getFileSuffix());
        fileRes.setUrl(this.getFilePath(fileId));
        return fileRes;
    }

    @Override
    public FileRes fileMoreUpload(FileMoreUpload upload, Long userId) {
        String fileName = upload.getFile().getOriginalFilename();//获取文件名
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);//获取文件后缀
        if (fileName.length() > 80) {
            fileName = fileName.substring(fileName.length() - 80);
        }
        if (!GlobalConstants.FILE_SUFFIX.contains(suffix)) {
            throw new BizException(SysConstant.File_Suffix_Incorrect);
        }
        if (StringUtils.isBlank(upload.getId()) || "0".equals(upload.getId())) {
            //上传到临时文件
            Long fileId = this.uploadTemp(upload.getFile(), userId);
            FileRes fileRes = new FileRes();
            fileRes.setFileName(fileName);
            fileRes.setFileSuffix(suffix);
            fileRes.setFileId(fileId);
            fileRes.setCreateTime(new Date());
            fileRes.setUrl(this.getFilePathTemp(fileId));
            return fileRes;
        } else {
            Long fileId = null;
            try {
                fileId = this.uploadFileToFormalPath(upload.getFile().getInputStream(), fileName);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            if (fileId == null) return null;
            FileMsg fileMsg = this.getFileMsg(fileId);
            FileRes fileRes = new FileRes();
            BeanUtils.copyProperties(fileMsg, fileRes);
            if (StringUtils.isNotBlank(StaticDict.File_Type.getText(upload.getType()))) {
                //退件单附件
                FileMoreDo fileMoreDo = new FileMoreDo();
                fileMoreDo.setMailId(Long.parseLong(upload.getId()));
                fileMoreDo.setType(upload.getType());
                fileMoreDo.setFileId(fileId);
                fileMoreDo.setCreateTime(new Date());
                fileMoreDao.insert(fileMoreDo);
                fileRes.setId(fileMoreDo.getId());
                fileRes.setCreateTime(fileMoreDo.getCreateTime());
            } else {
                return null;
            }
            return fileRes;
        }
    }

    @Override
    public List<FileVo> getFileVoByIds(String ids) {
        if (StringUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        String[] strings = ids.split(",");
        Long[] fileIds = Arrays.stream(strings).map(Long::valueOf).toArray(Long[]::new);
        List<FileDo> doList = this.lambdaQuery().in(FileDo::getId, Arrays.asList(fileIds)).list();
        List<FileVo> fileVos = JSONUtil.toList(JSONUtil.toJsonStr(doList), FileVo.class);
        if (CollUtil.isEmpty(fileVos)) {
            return Collections.emptyList();
        }
        Map<Long, UserDo> userIdMatch = userDao.selectBatchIds(fileVos.stream().map(FileVo::getCreateBy)
                .collect(Collectors.toList())).stream().collect(Collectors.toMap(UserDo::getId, val -> val));
        for (FileVo fileVo : fileVos) {
            fileVo.setUrl(this.getFilePath(fileVo.getId()));
            UserDo userDo = userIdMatch.get(fileVo.getCreateBy());
            if (userDo == null) {
                fileVo.setCreateByName("system");
            } else {
                fileVo.setCreateByName(userDo.getUserName());
            }
        }
        return fileVos;
    }

    @Override
    public void delFileAndAddLog(DeleteFileBo bo, Long userId, String username) {
        this.deleteFile(bo.getFileId());
        if (bo.getId() == null) {
            return;
        }
        operateRecordService.operateAddRecordAndValue(
                SysConstant.Delete_Attachment,
                "删除附件",
                bo.getId(),
                userId,
                username,
                bo.getType()
        );
        //删除主表内附件
        if (OperateTypeEnum.OrderInput == bo.getType()) {
            OrderInputDo inputDo = inputDao.selectById(bo.getId());
            if (inputDo != null) {
                String fileIds = inputDo.getFileIds();
                if (StrUtil.isNotBlank(fileIds)) {
                    String[] split = fileIds.split(",");
                    //删除主表内附件
                    inputDo.setFileIds(Arrays.stream(split).filter(i -> !i.equals(String.valueOf(bo.getFileId()))).map(String::valueOf).collect(Collectors.joining(",")));
                    inputDao.updateById(inputDo);
                }
            }
        } else if (OperateTypeEnum.OrderOutbound == bo.getType()) {
            OrderOutboundDo outboundDo = outboundDao.selectById(bo.getId());
            if (outboundDo != null) {
                String fileIds = outboundDo.getAppendixIds();
                if (StrUtil.isNotBlank(fileIds)) {
                    String[] split = fileIds.split(",");
                    //删除主表内附件
                    outboundDo.setAppendixIds(Arrays.stream(split).filter(i -> !i.equals(String.valueOf(bo.getFileId()))).map(String::valueOf).collect(Collectors.joining(",")));
                    outboundDao.updateById(outboundDo);
                }
            }
        }
    }
}
